package com.lkd.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.google.common.base.Strings;
import com.lkd.common.VMSystem;
import com.lkd.config.TopicConfig;
import com.lkd.contract.SupplyChannel;
import com.lkd.contract.SupplyContract;
import com.lkd.contract.TaskCompleteContract;
import com.lkd.dao.TaskDao;
import com.lkd.emq.MqttProducer;
import com.lkd.entity.TaskDetailsEntity;
import com.lkd.entity.TaskEntity;
import com.lkd.exception.LogicException;
import com.lkd.feign.UserService;
import com.lkd.feign.VMService;
import com.lkd.http.controller.BaseController;
import com.lkd.http.vo.CancelTaskViewModel;
import com.lkd.http.vo.TaskDetailsViewModel;
import com.lkd.http.vo.TaskViewModel;
import com.lkd.service.TaskDetailsService;
import com.lkd.service.TaskService;
import com.lkd.vo.Pager;
import com.lkd.vo.UserVO;
import com.lkd.vo.VmVO;
import lombok.extern.slf4j.Slf4j;
import org.checkerframework.checker.units.qual.A;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;

import java.time.Duration;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

@Service
@Slf4j
public class TaskServiceImpl extends ServiceImpl<TaskDao, TaskEntity> implements TaskService {

    @Autowired
    private VMService vmService;

    @Autowired
    private UserService userService;

    @Autowired
    private RedisTemplate redisTemplate;

    @Autowired
    private BaseController baseController;

    @Autowired
    private TaskDetailsService taskDetailsService;

    @Autowired
    private MqttProducer mqttProducer;

    @Override
    public Pager<TaskEntity> search(Long pageIndex, Long pageSize, String innerCode, Integer userId, String taskCode, Integer status, Boolean isRepair, String start, String end) {
        Page<TaskEntity> page = new Page<>(pageIndex, pageSize);
        LambdaQueryWrapper<TaskEntity> qw = new LambdaQueryWrapper<>();
        if (!Strings.isNullOrEmpty(innerCode)) {
            qw.eq(TaskEntity::getInnerCode, innerCode);
        }
        if (userId != null && userId > 0) {
            qw.eq(TaskEntity::getUserId, userId);
        }
        if (!Strings.isNullOrEmpty(taskCode)) {
            qw.like(TaskEntity::getTaskCode, taskCode);
        }
        if (status != null && status > 0) {
            qw.eq(TaskEntity::getTaskStatus, status);
        }
        if (isRepair != null) {
            if (isRepair) {
                qw.ne(TaskEntity::getProductTypeId, VMSystem.TASK_TYPE_SUPPLY);
            } else {
                qw.eq(TaskEntity::getProductTypeId, VMSystem.TASK_TYPE_SUPPLY);
            }
        }
        if (!Strings.isNullOrEmpty(start) && !Strings.isNullOrEmpty(end)) {
            qw
                    .ge(TaskEntity::getCreateTime, LocalDate.parse(start, DateTimeFormatter.ISO_LOCAL_DATE))
                    .le(TaskEntity::getCreateTime, LocalDate.parse(end, DateTimeFormatter.ISO_LOCAL_DATE));
        }
        //根据最后更新时间倒序排序
        qw.orderByDesc(TaskEntity::getUpdateTime);

        return Pager.build(this.page(page, qw));
    }

    /**
     * 创建工单
     *
     * @param taskViewModel
     * @return
     */
    @Override
    public boolean create(TaskViewModel taskViewModel) {
        //检验机器是否有未完成工单
        hasTask(taskViewModel);

        //检查参数及设备状态
        cheack(taskViewModel);

        //创建工单实体类并赋值
        TaskEntity taskEntity = new TaskEntity();
        BeanUtils.copyProperties(taskViewModel, taskEntity);

        //获取执行人信息
        UserVO user = userService.getUser(taskEntity.getUserId());

        taskEntity.setUserName(user.getUserName());
        taskEntity.setTaskCode(generateTaskCode());
        taskEntity.setProductTypeId(taskViewModel.getProductType());
        taskEntity.setTaskStatus(VMSystem.TASK_STATUS_CREATE);
        //设置区域信息
        VmVO vmInfo = vmService.getVMInfo(taskEntity.getInnerCode());
        taskEntity.setAddr(vmInfo.getNodeAddr());
        taskEntity.setRegionId(vmInfo.getRegionId());
        //保存工单
        this.save(taskEntity);
        //补货
        if (taskEntity.getProductTypeId() == VMSystem.TASK_TYPE_SUPPLY) {
            //获取补货详情放入工单详情表
            List<TaskDetailsViewModel> details = taskViewModel.getDetails();
            for (TaskDetailsViewModel detail : details) {
                TaskDetailsEntity detailsEntity = new TaskDetailsEntity();
                BeanUtils.copyProperties(detail, detailsEntity);
                detailsEntity.setTaskId(taskEntity.getTaskId());
                taskDetailsService.save(detailsEntity);
            }
        }

        //更新员工工单统计数量
        updateTaskZSet(taskEntity, 1);
        return true;
    }

    /**
     * 更新员工工单统计数量
     *
     * @param taskEntity
     * @param score
     */
    private void updateTaskZSet(TaskEntity taskEntity, int score) {
        //区分运营和运维工单
        String roleCode = "1003";
        if (taskEntity.getProductTypeId().intValue() == VMSystem.TASK_TYPE_SUPPLY) {
            roleCode = "1002";
        }
        String date = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMdd"));
        //构建key
        String key = VMSystem.REGION_TASK_KEY_PREF
                + date + "."
                + taskEntity.getRegionId() + "."
                + roleCode;
        redisTemplate.opsForZSet().incrementScore(key, taskEntity.getUserId(), score);
    }

    /**
     * 接受工单
     *
     * @param taskId
     * @return
     */
    @Override
    public boolean accept(Long taskId) {
        TaskEntity taskEntity = this.getById(taskId);
        if (taskEntity.getTaskStatus() != VMSystem.TASK_STATUS_CREATE) {
            throw new LogicException("工单已处理");
        }
        //判断工单执行人是否为登录用户
        if (taskEntity.getUserId() != baseController.getUserId()) {
            throw new LogicException("没有操作权限");
        }
        taskEntity.setTaskStatus(VMSystem.TASK_STATUS_PROGRESS);
        //更新状态
        this.updateById(taskEntity);

        return true;
    }

    /**
     * 取消工单
     *
     * @param taskId
     * @param cancelVM
     * @return
     */
    @Override
    public boolean cancelTask(Long taskId, CancelTaskViewModel cancelVM) {
        TaskEntity taskEntity = this.getById(taskId);
        //判断工单执行人是否为登录用户
        if (taskEntity.getUserId() != baseController.getUserId()) {
            throw new LogicException("没有操作权限");
        }
        if (taskEntity.getTaskStatus() == VMSystem.TASK_STATUS_FINISH || taskEntity.getTaskStatus() == VMSystem.TASK_STATUS_CANCEL) {
            throw new LogicException("工单已经结束");
        }

        //更新工单状态
        taskEntity.setTaskStatus(VMSystem.TASK_STATUS_CANCEL);
        taskEntity.setDesc(cancelVM.getDesc());
        this.updateById(taskEntity);

        //更新员工工单统计数量
        updateTaskZSet(taskEntity, -1);
        return true;
    }

    /**
     * 完成工单
     *
     * @param taskId
     * @return
     */
    @Override
    public boolean completeTask(Long taskId) {
        TaskEntity taskEntity = this.getById(taskId);
        //判断工单执行人是否为登录用户
        if (taskEntity.getUserId() != baseController.getUserId()) {
            throw new LogicException("没有操作权限");
        }
        if (taskEntity.getTaskStatus() == VMSystem.TASK_STATUS_FINISH || taskEntity.getTaskStatus() == VMSystem.TASK_STATUS_CANCEL) {
            throw new LogicException("工单已经结束");
        }
        taskEntity.setTaskStatus(VMSystem.TASK_STATUS_FINISH);
        this.updateById(taskEntity);

        //更改工单为完成状态后,向设备微服务发送消息,更新设备状态
        updateVMStatus(taskEntity);
        return true;
    }

    /**
     * 获取同一天内分配的工单最少的人
     *
     * @param regionId
     * @param isRepair 是否是运维工单
     * @return
     */
    @Override
    public int getLeastUser(Long regionId, Boolean isRepair) {
        String date = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMdd"));
        String roleCode = VMSystem.USER_OPERATOR;//运营人员
        //如果是运维人员
        if (isRepair) {
            roleCode = VMSystem.USER_REPAIRER;
        }
        //构建key
        String key = VMSystem.REGION_TASK_KEY_PREF
                + date + "."
                + regionId + "."
                + roleCode;
        Set<Object> set = redisTemplate.opsForZSet().range(key, 0, 1);
        if (StringUtils.isEmpty(set)) {
            throw new LogicException("没有相关人员");
        }
        return (int) set.stream().collect(Collectors.toList()).get(0);
    }

    //更新设备状态
    private void updateVMStatus(TaskEntity taskEntity) {
        //如果是投放或者撤机工单(运维工单)
        if (taskEntity.getProductTypeId() == VMSystem.TASK_TYPE_DEPLOY
                || taskEntity.getProductTypeId() == VMSystem.TASK_TYPE_REVOKE) {
            //封装实体类
            TaskCompleteContract taskCompleteContract = new TaskCompleteContract();
            taskCompleteContract.setInnerCode(taskEntity.getInnerCode());
            taskCompleteContract.setTaskType(taskEntity.getProductTypeId());
            try {
                //向设备微服务发送消息
                mqttProducer.send(TopicConfig.VMS_COMPLETED_TOPIC, 2, taskCompleteContract);
            } catch (JsonProcessingException e) {
                log.error("发送工单完成协议出错");
                throw new LogicException("发送工单完成协议出错");
            }
        }

        //如果是补货工单
        if (taskEntity.getProductTypeId() == VMSystem.TASK_TYPE_SUPPLY) {
            SupplyContract supplyContract = new SupplyContract();
            supplyContract.setInnerCode(taskEntity.getInnerCode());

            //查找补货数据并封装
            List<SupplyChannel> supplyChannels = new ArrayList<>();
            //从工单明细表查找补货数据
            LambdaQueryWrapper<TaskDetailsEntity> wrapper = new LambdaQueryWrapper<>();
            wrapper.eq(TaskDetailsEntity::getTaskId, taskEntity.getTaskId());
            List<TaskDetailsEntity> details = taskDetailsService.list(wrapper);
            for (TaskDetailsEntity detail : details) {
                //封装补货信息
                SupplyChannel supplyChannel = new SupplyChannel();
                supplyChannel.setChannelId(detail.getChannelCode());
                supplyChannel.setCapacity(detail.getExpectCapacity());
                supplyChannels.add(supplyChannel);
            }
            supplyContract.setSupplyData(supplyChannels);
            //发送EMQ消息
            try {
                mqttProducer.send(TopicConfig.VMS_SUPPLY_TOPIC, 2, supplyContract);
            } catch (JsonProcessingException e) {
                log.error("发送工单完成协议出错");
                throw new LogicException("发送工单完成协议出错");
            }
        }
    }

    //生成工单编号
    private String generateTaskCode() {
        //日期字符串
        String date = LocalDate.now().format(DateTimeFormatter.ofPattern("yyyyMMdd"));
        String key = "lkd.task.code." + date;
        //是否已有编号
        Object obj = redisTemplate.opsForValue().get(key);
        //如果没有,也就是第一个编号,设置起始值,过期时间为1天
        if (obj == null) {
            redisTemplate.opsForValue().setIfAbsent(key, 0L, Duration.ofDays(1));
            String code = date + "0001";
            log.info("编号为:{}", code);
            return code;
        }
        String code = date + String.format("%04d", redisTemplate.opsForValue().increment(key, 1));
        log.info("编号为:{}", code);
        return code;
    }

    //检验机器是否有未完成工单
    private void hasTask(TaskViewModel taskViewModel) {
        String innerCode = taskViewModel.getInnerCode();
        int productType = taskViewModel.getProductType();

        LambdaQueryWrapper<TaskEntity> wrapper = new LambdaQueryWrapper<>();
        wrapper.select(TaskEntity::getTaskId)
                .eq(TaskEntity::getInnerCode, innerCode)
                .eq(TaskEntity::getProductTypeId, productType)
                .le(TaskEntity::getTaskStatus, VMSystem.TASK_STATUS_PROGRESS);
        if (this.count(wrapper) > 0) {
            throw new LogicException("该设备有未完成的工单");
        }
    }

    //检查参数及设备状态
    private void cheack(TaskViewModel taskViewModel) {
        //传参校验
        if (taskViewModel == null) {
            throw new LogicException("请求参数为空");
        }
        //设备相关
        VmVO vmInfo = vmService.getVMInfo(taskViewModel.getInnerCode());
        if (vmInfo == null) {
            throw new LogicException("设备不存在");
        }
        //校验设备状态
        int productType = taskViewModel.getProductType();
        if (productType == VMSystem.TASK_TYPE_DEPLOY && vmInfo.getVmStatus() == VMSystem.VM_STATUS_RUNNING) {
            throw new LogicException("设备正在运营");
        }
        if (productType == VMSystem.TASK_TYPE_SUPPLY && vmInfo.getVmStatus() != VMSystem.VM_STATUS_RUNNING) {
            throw new LogicException("设备未运营");
        }
        if (productType == VMSystem.TASK_TYPE_REVOKE && vmInfo.getVmStatus() != VMSystem.VM_STATUS_RUNNING) {
            throw new LogicException("设备未运营");
        }
    }

}
